import { useState, useEffect, useRef } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { ArrowLeft, RotateCcw } from 'lucide-react';
import { modelAPI } from '../api/api';
import stateAPI from '../api/stateApi';
import { getTemplate } from '../templates';
import { drawPoint, drawLineById, drawLabel, drawPerpendicularById, drawRayById, DRAWING_STYLES } from '../utils/drawingUtils';
import { projectPointOnLine, updateSlidingPoints, updatePerpendicularFootPoints, updateFixedPoints, applyDragConstraints, applySlidingConstraints, calculateExpressionValue, parseExpressionForRender, groupAndAverageValues, getAdjustedDisplayValue } from '../utils/expressionUtils';
import { calculateConstantValue, SCALE, updateIntersectionPoints, convertToMathSymbols } from '../utils/canvasUtils';
import './GeometryModelView.css';

function GeometryModelView() {
  const { id } = useParams();
  const navigate = useNavigate();
  const canvasRef = useRef(null);
  const containerRef = useRef(null);
  
  const [model, setModel] = useState(null);
  const [geometryData, setGeometryData] = useState(null);
  const [dragging, setDragging] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const [isSpecialModel, setIsSpecialModel] = useState(false);
  const [TemplateComponent, setTemplateComponent] = useState(null);
  
  // 状态管理
  const [modelStates, setModelStates] = useState([]); // 所有状态
  const [currentStateId, setCurrentStateId] = useState(null); // 当前选中的状态ID（null = 母版）
  const [masterData, setMasterData] = useState(null); // 母版数据
  
  // 固定工作空间尺寸
  const WORKSPACE_WIDTH = 800;
  const WORKSPACE_HEIGHT = 500;

  // 加载模型数据
  useEffect(() => {
    const loadModel = async () => {
      try {
        setLoading(true);
        const data = await modelAPI.getById(id);
        setModel(data);
        const parsedData = JSON.parse(data.canvas_data);
        
        setGeometryData(parsedData);
        setMasterData(parsedData); // 保存母版数据
        
        // 加载模型的所有状态
        try {
          const states = await stateAPI.getModelStates(id);
          setModelStates(states);
        } catch (err) {
          console.error('加载状态失败:', err);
        }
        
        // 检查是否是特殊模型
        if (data.is_special && data.template_path) {
          setIsSpecialModel(true);
          const template = getTemplate(data.template_path);
          setTemplateComponent(() => template);
        } else {
          setIsSpecialModel(false);
          setTemplateComponent(null);
        }
        
        setError(null);
      } catch (err) {
        setError('加载模型失败: ' + err.message);
      } finally {
        setLoading(false);
      }
    };
    loadModel();
  }, [id]);
  
  // 小屏幕旋转处理
  useEffect(() => {
    const isSmallScreen = window.innerWidth <= 480;
    
    if (isSmallScreen) {
      // 防止滚动条
      document.body.style.overflow = 'hidden';
      document.documentElement.style.overflow = 'hidden';
      document.body.style.width = '100vw';
      document.body.style.height = '100vh';
    } else {
      document.body.style.overflow = '';
      document.documentElement.style.overflow = '';
      document.body.style.width = '';
      document.body.style.height = '';
    }
    
    return () => {
      document.body.style.overflow = '';
      document.documentElement.style.overflow = '';
      document.body.style.width = '';
      document.body.style.height = '';
    };
  }, []);

  // 自动更新垂足点（当相关点移动时）
  useEffect(() => {
    if (!geometryData || !geometryData.perpendiculars || geometryData.perpendiculars.length === 0) {
      return;
    }
    
    // 获取所有非垂足点的位置签名（用于检测变化）
    const nonFootPointsKey = geometryData.points
      .filter(p => !p.isFootPoint)
      .map(p => `${p.id}:${p.x.toFixed(2)}:${p.y.toFixed(2)}`)
      .sort()
      .join('|');
    
    // 更新所有垂足点（不传入draggedPointId，更新所有）
    const updatedPoints = updatePerpendicularFootPoints(geometryData.points, geometryData.perpendiculars);
    
    // 检查是否有垂足点位置发生变化
    let hasChanges = false;
    for (const newPoint of updatedPoints) {
      if (newPoint.isFootPoint) {
        const oldPoint = geometryData.points.find(p => p.id === newPoint.id);
        if (!oldPoint) {
          hasChanges = true;
          break;
        }
        // 使用较小的阈值比较浮点数
        if (Math.abs(oldPoint.x - newPoint.x) > 0.01 || Math.abs(oldPoint.y - newPoint.y) > 0.01) {
          hasChanges = true;
          break;
        }
      }
    }
    
    // 只有当垂足点位置确实发生变化时才更新状态
    if (hasChanges) {
      setGeometryData(prev => {
        // 再次检查非垂足点是否变化（防止无限循环）
        const prevNonFootPointsKey = prev.points
          .filter(p => !p.isFootPoint)
          .map(p => `${p.id}:${p.x.toFixed(2)}:${p.y.toFixed(2)}`)
          .sort()
          .join('|');
        
        // 如果非垂足点没有变化，说明只是垂足点需要更新
        if (prevNonFootPointsKey === nonFootPointsKey) {
          return {
            ...prev,
            points: updatedPoints
          };
        }
        return prev;
      });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [geometryData?.perpendiculars, geometryData?.points?.filter(p => !p.isFootPoint).map(p => `${p.id}:${p.x}:${p.y}`).join('|')]);

  // 窗口大小变化时重新绘制
  useEffect(() => {
    const handleResize = () => {
      if (canvasRef.current && geometryData && !isSpecialModel) {
        drawCanvas();
      }
    };

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [geometryData, isSpecialModel]);

  // 绘制画布 - 等待 canvas 元素挂载后再绘制
  useEffect(() => {
    // 确保数据已加载、不是特殊模型、且不在loading状态
    if (geometryData && !isSpecialModel && !loading) {
      // 使用 setTimeout 确保 canvas 元素已经渲染到 DOM
      const timer = setTimeout(() => {
        if (canvasRef.current) {
          drawCanvas();
        }
      }, 0);
      
      return () => clearTimeout(timer);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [geometryData, isSpecialModel, loading]);

  const drawCanvas = () => {
    const canvas = canvasRef.current;
    if (!canvas || !geometryData) return;
    
    // 根据屏幕尺寸决定画布尺寸（保持 8:5 比例）
    const windowWidth = window.innerWidth;
    const windowHeight = window.innerHeight;
    const isLandscape = windowWidth > windowHeight;
    let displayWidth, displayHeight;
    
    // 根据横竖屏选择参考尺寸
    const referenceSize = isLandscape ? windowHeight : windowWidth;
    
    if (referenceSize <= 375) {
      // 小屏幕：360x225
      displayWidth = 360;
      displayHeight = 225;
    } else if (referenceSize <= 560) {
      // 中等屏幕：根据参考尺寸线性插值
      // 从 360x225 到 560x350
      const ratio = (referenceSize - 375) / (560 - 375);
      displayWidth = Math.round(360 + ratio * (560 - 360));
      displayHeight = Math.round(225 + ratio * (350 - 225));
    } else {
      // 大屏幕：原始尺寸 800x500
      displayWidth = WORKSPACE_WIDTH;
      displayHeight = WORKSPACE_HEIGHT;
    }
    
    // 设置高DPI
    const dpr = window.devicePixelRatio || 1;
    canvas.width = displayWidth * dpr;
    canvas.height = displayHeight * dpr;
    canvas.style.width = `${displayWidth}px`;
    canvas.style.height = `${displayHeight}px`;
    
    const ctx = canvas.getContext('2d');
    ctx.scale(dpr, dpr);
    
    // 如果不是原始尺寸，需要整体缩放绘制内容
    if (displayWidth !== WORKSPACE_WIDTH) {
      const scale = displayWidth / WORKSPACE_WIDTH;
      ctx.scale(scale, scale);
    }
    
    // 清空画布
    ctx.clearRect(0, 0, WORKSPACE_WIDTH, WORKSPACE_HEIGHT);
    
    // 绘制背景
    ctx.fillStyle = '#ffffff';
    ctx.fillRect(0, 0, WORKSPACE_WIDTH, WORKSPACE_HEIGHT);

    // 绘制填充的面积区域（最先绘制，作为背景）
    if (geometryData.constants && Array.isArray(geometryData.constants)) {
      geometryData.constants.forEach(constant => {
      if (constant.type === 'area' && constant.fill && constant.fillColor && constant.pointIds && constant.pointIds.length >= 3) {
        const points = constant.pointIds.map(id => 
          geometryData.points.find(p => p.id === id)
        ).filter(Boolean);
        
        if (points.length >= 3) {
          ctx.save();
          ctx.fillStyle = constant.fillColor;
          ctx.beginPath();
          ctx.moveTo(points[0].x, points[0].y);
          for (let i = 1; i < points.length; i++) {
            ctx.lineTo(points[i].x, points[i].y);
          }
          ctx.closePath();
          ctx.fill();
          ctx.restore();
        }
      }
    });
    }

    // 绘制连线
    if (geometryData.lines && Array.isArray(geometryData.lines)) {
    geometryData.lines.forEach(line => {
      drawLineById(ctx, geometryData.points, line, { offsetX: 0, offsetY: 0 }, false);
    });
    }

    // 绘制垂线
    if (geometryData.perpendiculars && Array.isArray(geometryData.perpendiculars)) {
      geometryData.perpendiculars.forEach(perpendicular => {
        if (perpendicular.style === 'hidden') return;
        
        drawPerpendicularById(ctx, geometryData.points, perpendicular, { offsetX: 0, offsetY: 0 }, {
          drawExtension: true,
          extensionStyle: { style: 'dashed', color: '#999', width: 1 },
          perpendicularStyle: { 
            style: perpendicular.style || 'dashed', 
            color: perpendicular.color || '#666', 
            width: perpendicular.width || 2 
          },
          drawFootMark: perpendicular.showFoot !== false,
          footMarkSize: 14  // 与工作台保持一致
        });
      });
    }

    // 绘制平行线
    if (geometryData.parallels && Array.isArray(geometryData.parallels)) {
      geometryData.parallels.forEach(parallel => {
        if (parallel.style === 'hidden') return;

        const fromPoint = geometryData.points.find(p => p.id === parallel.fromPointId);
        const lineStart = geometryData.points.find(p => p.id === parallel.toLineStartId);
        const lineEnd = geometryData.points.find(p => p.id === parallel.toLineEndId);

        if (fromPoint && lineStart && lineEnd) {
          const dx = lineEnd.x - lineStart.x;
          const dy = lineEnd.y - lineStart.y;
          const length = Math.sqrt(dx * dx + dy * dy);
          const unitDx = dx / length;
          const unitDy = dy / length;
          const extensionDistance = 5000;

          const parallelStart = {
            x: fromPoint.x - unitDx * extensionDistance,
            y: fromPoint.y - unitDy * extensionDistance
          };
          const parallelEnd = {
            x: fromPoint.x + unitDx * extensionDistance,
            y: fromPoint.y + unitDy * extensionDistance
          };

          ctx.save();
          ctx.strokeStyle = parallel.color || DRAWING_STYLES.line.color;
          ctx.lineWidth = parallel.width || DRAWING_STYLES.line.width;
          if (parallel.style === 'dashed') {
            ctx.setLineDash([5, 5]);
          }
          ctx.beginPath();
          ctx.moveTo(parallelStart.x, parallelStart.y);
          ctx.lineTo(parallelEnd.x, parallelEnd.y);
          ctx.stroke();
          ctx.restore();
        }
      });
    }

    // 绘制射线
    if (geometryData.rays && Array.isArray(geometryData.rays)) {
      geometryData.rays.forEach(ray => {
        if (ray.style === 'hidden') return;

        drawRayById(ctx, geometryData.points, ray, { offsetX: 0, offsetY: 0 }, false);
      });
    }

    // 绘制标签
    if (geometryData.labels && Array.isArray(geometryData.labels)) {
    geometryData.labels.forEach(label => {
      // 如果是面积标签，从关联的常量获取符号转换设置
      let enableSymbolConversion = true;
      if (label.isAreaLabel && label.linkedConstantId) {
        const constant = geometryData.constants?.find(c => c.id === label.linkedConstantId);
        if (constant) {
          enableSymbolConversion = constant.enableSymbolConversion !== false;
        }
      }
      drawLabel(ctx, { ...label, enableSymbolConversion }, { offsetX: 0, offsetY: 0 }, false);
    });
    }

    // 绘制点
    if (geometryData.points && Array.isArray(geometryData.points)) {
    geometryData.points.forEach(point => {
      // 在用户端隐藏所有交点
      if (point.isIntersection === true) {
        // 交点只绘制标签（如果有）
        if (point.label) {
          ctx.save();
          const labelOffset = point.labelOffset || { x: 0, y: 0 };
          ctx.fillStyle = DRAWING_STYLES.label.color;
          ctx.font = DRAWING_STYLES.label.italicFont;
          ctx.fillText(
            point.label, 
            point.x + DRAWING_STYLES.label.offsetX + labelOffset.x, 
            point.y + DRAWING_STYLES.label.offsetY + labelOffset.y
          );
          ctx.restore();
        }
        return; // 跳过绘制交点本身
      }
      
      // 检查是否是隐藏点
      if (point.hidden === true) {
        // 隐藏点只绘制标签，使用相对偏移量（与 drawPoint 函数一致）
        if (point.label) {
          ctx.save();
          const labelOffset = point.labelOffset || { x: 0, y: 0 };
          ctx.fillStyle = DRAWING_STYLES.label.color;
          ctx.font = DRAWING_STYLES.label.italicFont;
          ctx.fillText(
            point.label, 
            point.x + DRAWING_STYLES.label.offsetX + labelOffset.x, 
            point.y + DRAWING_STYLES.label.offsetY + labelOffset.y
          );
          ctx.restore();
        }
      } else {
        // 所有点（可拖动和不可拖动）都使用 drawPoint 函数
        // drawPoint 会自动处理 labelOffset
        drawPoint(ctx, point, { offsetX: 0, offsetY: 0 }, false);
      }
    });
    }
  };
  
  // 切换到指定状态
  const handleSwitchState = (stateId) => {
    const state = modelStates.find(s => s.id === stateId);
    if (state) {
      setCurrentStateId(stateId);
      setGeometryData(state.canvas_data);
      setDragging(null);
    }
  };

  // 获取画布坐标
  const getCanvasCoords = (e) => {
    const canvas = canvasRef.current;
    const rect = canvas.getBoundingClientRect();
    
    const clientX = e.touches ? e.touches[0].clientX : e.clientX;
    const clientY = e.touches ? e.touches[0].clientY : e.clientY;
    
    // 检查是否是小屏幕旋转模式
    const isSmallScreen = window.innerWidth <= 480;
    
    if (isSmallScreen) {
      // 小屏幕旋转模式：容器旋转了90度（顺时针）
      // canvas的getBoundingClientRect()返回的是旋转后在屏幕上的实际位置
      
      // 计算点击位置相对于canvas的位置（在屏幕坐标系中）
      const relativeX = clientX - rect.left;
      const relativeY = clientY - rect.top;
      
      // 旋转90度（顺时针）的逆变换
      // 旋转90度：原坐标(x, y) -> 旋转后(y, -x)
      // 逆变换：旋转后坐标(x', y') -> 原坐标(-y', x')
      // 但由于Y轴方向（向下为正），实际变换为：
      const originalX = relativeY;
      const originalY = rect.width - relativeX;
      
      // 根据当前画布尺寸，将坐标映射回原始工作空间
      // 注意：旋转后canvas的显示宽度是rect.height，高度是rect.width
      const scale = rect.height / WORKSPACE_WIDTH;
      
      return {
        x: originalX / scale,
        y: originalY / scale
      };
    }
    
    // 正常模式：原有逻辑
    // 计算相对于canvas的坐标
    const relativeX = clientX - rect.left;
    const relativeY = clientY - rect.top;
    
    // 根据当前画布尺寸，将坐标映射回原始工作空间
    const windowWidth = window.innerWidth;
    const windowHeight = window.innerHeight;
    const isLandscape = windowWidth > windowHeight;
    const referenceSize = isLandscape ? windowHeight : windowWidth;
    
    let scale = 1;
    
    if (referenceSize <= 375) {
      scale = 360 / WORKSPACE_WIDTH;
    } else if (referenceSize <= 560) {
      const ratio = (referenceSize - 375) / (560 - 375);
      const displayWidth = Math.round(360 + ratio * (560 - 360));
      scale = displayWidth / WORKSPACE_WIDTH;
    }
    
    return {
      x: relativeX / scale,
      y: relativeY / scale
    };
  };

  // 查找被点击的点
  const findClickedPoint = (x, y) => {
    for (const point of geometryData.points) {
      const dist = Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));
      if (dist < 22) {  // 增大点击检测范围以匹配更大的点
        return point;
      }
    }
    return null;
  };

  // 鼠标按下
  const handleMouseDown = (e) => {
    const { x, y } = getCanvasCoords(e);
    const point = findClickedPoint(x, y);
      if (point && point.draggable !== false) {
        setDragging(point);
    }
  };

  // 鼠标移动
  const handleMouseMove = (e) => {
    if (!dragging) return;
    
    const { x, y } = getCanvasCoords(e);
    
    // 检查拖动的点是否是滑动点
      if (dragging.constrainedToLine) {
        setGeometryData(prev => {
          const { startPointId, endPointId } = dragging.constrainedToLine;
          let startPoint = prev.points.find(p => p.id === startPointId);
          let endPoint = prev.points.find(p => p.id === endPointId);
          
          if (!startPoint || !endPoint) return prev;
          
          let updatedPoints = [...prev.points];
          
          const isFootPoint = prev.perpendiculars?.some(perp => perp.footPointId === dragging.id);
        if (isFootPoint) return prev;
        
        // 首先应用拖动约束
        const constrainedCoords = applyDragConstraints(
          dragging,
          { x, y },
          prev.dragConstraints || [],
          prev.lines,
          prev.points
        );
        
        const projection = projectPointOnLine(constrainedCoords, startPoint, endPoint);
          let finalRatio = projection.t;
          
        // 碰撞检测
          const pointsOnSameLine = updatedPoints.filter(p => {
          if (p.id === dragging.id) return false;
            if (p.constrainedToLine && 
                p.constrainedToLine.startPointId === dragging.constrainedToLine.startPointId &&
                p.constrainedToLine.endPointId === dragging.constrainedToLine.endPointId) {
              return true;
            }
            if (p.id === startPointId || p.id === endPointId) {
              return true;
            }
            return false;
          });
          
          const pointRatios = pointsOnSameLine.map(p => {
            if (p.id === startPointId) return 0;
            if (p.id === endPointId) return 1;
            return p.constrainedToLine?.ratio || 0;
          }).sort((a, b) => a - b);

          if (pointRatios.length > 0) {
            let leftBound = 0;
            let rightBound = 1;

            for (const ratio of pointRatios) {
              if (ratio < finalRatio && ratio > leftBound) {
                leftBound = ratio;
              }
              if (ratio > finalRatio && ratio < rightBound) {
                rightBound = ratio;
              }
            }

            const buffer = 0.001;
            finalRatio = Math.max(leftBound + buffer, Math.min(rightBound - buffer, finalRatio));
          }

          // 应用滑动点约束：防止滑动点超出约束点
          finalRatio = applySlidingConstraints(
            dragging,
            finalRatio,
            prev.slidingConstraints || [],
            updatedPoints
          );

          const finalX = startPoint.x + finalRatio * (endPoint.x - startPoint.x);
          const finalY = startPoint.y + finalRatio * (endPoint.y - startPoint.y);
          
          updatedPoints = updatedPoints.map(p =>
            p.id === dragging.id ? { 
              ...p, 
              x: finalX, 
              y: finalY,
              constrainedToLine: { ...p.constrainedToLine, ratio: finalRatio }
            } : p
          );
          
          updatedPoints = updateSlidingPoints(updatedPoints, dragging.id);

          // 更新所有交点位置（确保交点标签跟随移动）
          const pointsWithUpdatedIntersections = updateIntersectionPoints(updatedPoints, prev.lines, prev.parallels || [], prev.rays || []);

          // 更新面积标签位置
          const updatedLabels = prev.labels.map(label => {
            if (label.isAreaLabel) {
              const constant = prev.constants.find(c => c.id === label.linkedConstantId);
              if (constant && constant.pointIds) {
                const allPoints = constant.pointIds.map(id =>
                  pointsWithUpdatedIntersections.find(p => p.id === id)
                ).filter(Boolean);

                if (allPoints.length > 0) {
                  let sumX = 0, sumY = 0;
                  allPoints.forEach(p => {
                    sumX += p.x;
                    sumY += p.y;
                  });
                  return {
                    ...label,
                    x: sumX / allPoints.length,
                    y: sumY / allPoints.length
                  };
                }
              }
            }
            return label;
          });

          return {
            ...prev,
            points: pointsWithUpdatedIntersections,
            labels: updatedLabels
          };
        });
      } else {
        // 普通点，自由拖动
        setGeometryData(prev => {
          const constrainedCoords = applyDragConstraints(
            dragging,
          { x, y },
            prev.dragConstraints || [],
            prev.lines,
            prev.points
          );
          
          let updatedPoints = prev.points.map(p =>
            p.id === dragging.id ? { ...p, x: constrainedCoords.x, y: constrainedCoords.y } : p
          );
          
          updatedPoints = updateSlidingPoints(updatedPoints, dragging.id);
          updatedPoints = updateFixedPoints(updatedPoints, dragging.id);
          const finalPoints = updatePerpendicularFootPoints(updatedPoints, prev.perpendiculars, dragging.id);
          
          // 更新所有交点位置（确保交点标签跟随移动）
          const pointsWithUpdatedIntersections = updateIntersectionPoints(finalPoints, prev.lines, prev.parallels || [], prev.rays || []);
          
        // 更新面积标签位置
          const updatedLabels = prev.labels.map(label => {
            if (label.isAreaLabel) {
              const constant = prev.constants.find(c => c.id === label.linkedConstantId);
              if (constant && constant.pointIds) {
                const allPoints = constant.pointIds.map(id => 
                  finalPoints.find(p => p.id === id)
                ).filter(Boolean);
                
                if (allPoints.length > 0) {
                  let sumX = 0, sumY = 0;
                  allPoints.forEach(p => {
                    sumX += p.x;
                    sumY += p.y;
                  });
                  return {
                    ...label,
                    x: sumX / allPoints.length,
                    y: sumY / allPoints.length
                  };
                }
              }
            }
            return label;
          });
          
          return {
            ...prev,
            points: pointsWithUpdatedIntersections,
            labels: updatedLabels
          };
        });
    }
  };

  // 鼠标释放
  const handleMouseUp = () => {
    setDragging(null);
  };

  // 重置
  const handleReset = () => {
    if (masterData) {
      setGeometryData(masterData);
      setCurrentStateId(null);
      setDragging(null);
    }
  };

  if (loading) {
    return (
      <div className="geometry-model-view">
        <div className="loading-container">
          <div className="spinner"></div>
          <p>加载中...</p>
        </div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="geometry-model-view">
        <div className="error-container">
          <p>{error}</p>
          <button onClick={() => navigate(-1)} className="btn btn-secondary">
            <ArrowLeft size={16} />
            返回
          </button>
        </div>
      </div>
    );
  }

  if (!geometryData) return null;

  // 如果是特殊模型，使用特殊模板渲染（不渲染外层容器）
  if (isSpecialModel && TemplateComponent) {
    return (
      <TemplateComponent 
        canvasData={geometryData}
        onDataChange={(newData) => setGeometryData(newData)}
        modelTitle={model?.title || ''}
      />
    );
  }

  // 普通几何模型使用DOM布局
  return (
    <div className="geometry-model-view full-screen">
      <div className="main-container">
        {/* 顶部：Logo 和标题（带背景） */}
        <div className="top-bar">
          <button 
            onClick={() => navigate('/')} 
            className="back-button"
            aria-label="返回列表"
          >
            <ArrowLeft size={20} />
            <span className="back-text">返回</span>
          </button>
          <h1 className="app-title">{model?.title || '几何画板'}</h1>
          <img src="/geometry/logo.png" alt="Logo" className="app-logo" />
        </div>

        {/* 主体内容：左中右三栏 */}
        {(() => {
          // 收集所有数值用于误差调整
          const allValues = [];
          
          // 获取常量值的函数（用于表达式计算）
          const getConstantValue = (name) => {
            const c = geometryData.constants?.find(c => c.name === name);
            if (!c) return null;
            const val = calculateConstantValue(c, geometryData.points, geometryData.constants);
            const num = parseFloat(val);
            return isNaN(num) ? null : num;
          };
          
          // 辅助函数：判断表达式是否只包含面积类型的常量
          const isExpressionOnlyArea = (expression) => {
            if (!expression.parts || expression.parts.length === 0) return false;
            for (const part of expression.parts) {
              if (part.type === 'constant') {
                const constant = geometryData.constants.find(c => c.name === part.name);
                if (!constant || constant.type !== 'area') {
                  return false;
                }
              }
            }
            return true;
          };

          // 只收集面积类型的常量值
          if (geometryData.constants && geometryData.constants.length > 0) {
            geometryData.constants.forEach(constant => {
              if (constant.type === 'area') {
                const rawValue = calculateConstantValue(constant, geometryData.points, geometryData.constants);
                const num = parseFloat(rawValue);
                if (!isNaN(num)) {
                  allValues.push(num);
                }
              }
            });
          }
          
          // 只收集只包含面积常量的表达式结果值
          if (geometryData.expressions && geometryData.expressions.length > 0) {
            geometryData.expressions.forEach(expression => {
              if (isExpressionOnlyArea(expression)) {
                const result = calculateExpressionValue(expression, getConstantValue);
                const num = parseFloat(result);
                if (!isNaN(num)) {
                  allValues.push(num);
                }
              }
            });
          }
          
          // 生成值调整映射（误差在1之内的取平均，仅针对面积）
          const valueMap = groupAndAverageValues(allValues, 1.0);
          
          return (
            <>
        <div className="content-layout">
          {/* 左侧：常量监控 */}
          <div className="left-sidebar">
            {geometryData.constants && geometryData.constants.length > 0 && (
              <>
                {geometryData.constants.map((constant) => {
                  const rawValue = calculateConstantValue(constant, geometryData.points, geometryData.constants);
                  const num = parseFloat(rawValue);
                  
                  // 只对面积类型的常量使用调整后的值（考虑误差）
                  let adjustedValue = rawValue;
                  if (!isNaN(num)) {
                    if (constant.type === 'area') {
                      adjustedValue = getAdjustedDisplayValue(num, valueMap, 2);
                    } else {
                      // 非面积类型直接使用原始值
                      adjustedValue = num.toFixed(2);
                    }
                    // 如果是角度，添加度数符号
                    if (rawValue.includes('°')) {
                      adjustedValue += '°';
                    }
                  }
                  
                  // 数学符号转换（与面积标签保持一致）
                  const formatConstantName = (name) => {
                    const enableConversion = constant.enableSymbolConversion !== false;
                    // 面积常量：S + 下标
                    if (constant.type === 'area') {
                      // 移除开头的 S，对剩余内容进行数学符号转换
                      const content = name.replace(/^[Ss]+/g, '');
                      const mathContent = convertToMathSymbols(content, enableConversion);
                      return (
                        <>
                          S<sub>{mathContent}</sub>
                        </>
                      );
                    }
                    // 非面积常量：直接转换数学符号
                    return convertToMathSymbols(name, enableConversion);
                  };
                  
                  return (
                    <div key={constant.id} className="constant-item">
                      <span className="constant-name">
                        {formatConstantName(constant.name)}
                      </span>
                      <span className="constant-equals"> = </span>
                      <span className="constant-value">{adjustedValue}</span>
                    </div>
                  );
                })}
              </>
            )}
          </div>

          {/* 中间：Canvas */}
          <div className="center-content">
            {/* Canvas画布 */}
            <div className="canvas-container" ref={containerRef}>
              <canvas
                ref={canvasRef}
                onMouseDown={handleMouseDown}
                onMouseMove={handleMouseMove}
                onMouseUp={handleMouseUp}
                onMouseLeave={handleMouseUp}
                onTouchStart={handleMouseDown}
                onTouchMove={handleMouseMove}
                onTouchEnd={handleMouseUp}
                style={{ 
                  cursor: dragging ? 'grabbing' : 'default',
                  touchAction: 'none'
                }}
              />
            </div>
          </div>

          {/* 右侧：按钮 */}
          <div className="right-sidebar">
            {modelStates.map((state) => (
              <button
                key={state.id}
                onClick={() => handleSwitchState(state.id)}
                className={`btn ${currentStateId === state.id ? 'btn-primary' : 'btn-secondary'}`}
              >
                {state.button_label}
              </button>
            ))}
            <button onClick={handleReset} className="btn btn-primary">
              重置
            </button>
          </div>
        </div>

        {/* 底部：提示和表达式（贯穿整个底部） */}
        <div className="bottom-info">
          {/* 第一个：蓝点可拖动提示 */}
          <div className="drag-hint">
            <span style={{ color: '#007AFF' }}>●</span> 蓝色点可拖动
          </div>

          {/* 后面：表达式列表 */}
          {geometryData.expressions && geometryData.expressions.length > 0 && (
            <>
              {geometryData.expressions.map((expression) => {
                const result = calculateExpressionValue(expression, getConstantValue);
                
                // 只对只包含面积常量的表达式使用调整后的值（考虑误差）
                let adjustedResult = result;
                if (result !== null && result !== undefined) {
                  const num = parseFloat(result);
                  if (!isNaN(num)) {
                    if (isExpressionOnlyArea(expression)) {
                      adjustedResult = getAdjustedDisplayValue(num, valueMap, 2);
                    } else {
                      // 非面积表达式直接使用原始值
                      adjustedResult = num.toFixed(2);
                    }
                  }
                }
                
                // 使用 parseExpressionForRender 解析表达式（支持分式显示）
                const parsedElements = parseExpressionForRender(expression, geometryData.constants || []);
                
                // 数学符号转换函数（与面积标签保持一致）
                const formatMathName = (name, isArea = false, constantName = null) => {
                  // 查找常量以获取符号转换设置
                  const constant = constantName 
                    ? geometryData.constants.find(c => c.name === constantName)
                    : geometryData.constants.find(c => c.name === name);
                  const enableConversion = constant?.enableSymbolConversion !== false;
                  
                  if (isArea) {
                    // 面积常量：移除开头的 S，对剩余内容进行数学符号转换
                    const content = name.replace(/^[Ss]+/g, '');
                    const mathContent = convertToMathSymbols(content, enableConversion);
                    return `S${mathContent}`;
                  }
                  // 非面积常量：直接转换数学符号
                  return convertToMathSymbols(name, enableConversion);
                };
                
                // 渲染解析后的表达式元素
                const renderElement = (element, index) => {
                  if (element.type === 'fraction') {
                    // 渲染分式
                    const renderParts = (parts) => {
                      return parts.map((p, partIdx) => {
                        if (p.type === 'constant') {
                          if (p.isArea) {
                            const formattedName = formatMathName(p.name, true, p.name);
                            const match = formattedName.match(/^S(.+)$/);
                            if (match) {
                              return <span key={partIdx}>S<sub>{match[1]}</sub></span>;
                            }
                            return <span key={partIdx}>{formattedName}</span>;
                          }
                          const formattedName = formatMathName(p.name, false, p.name);
                          return <span key={partIdx}>{formattedName}</span>;
                        } else if (p.type === 'number') {
                          return <span key={partIdx}>{p.value}</span>;
                        } else if (p.type === 'operator') {
                          return <span key={partIdx}> {p.symbol} </span>;
                        }
                        return null;
                      });
                    };
                    
                    return (
                      <span key={index} className="fraction">
                        <span className="fraction-numerator">
                          {renderParts(element.numerator)}
                        </span>
                        <span className="fraction-line"></span>
                        <span className="fraction-denominator">
                          {renderParts(element.denominator)}
                        </span>
                      </span>
                    );
                  } else if (element.type === 'constant') {
                    const constant = geometryData.constants.find(c => c.name === element.name);
                    const enableConversion = constant?.enableSymbolConversion !== false;
                    
                    if (element.isArea) {
                      const content = element.name.replace(/^[Ss]+/g, '');
                      const convertedContent = convertToMathSymbols(content, enableConversion);
                      return <span key={index}>S<sub>{convertedContent}</sub></span>;
                    }
                    const convertedName = convertToMathSymbols(element.name, enableConversion);
                    return <span key={index}>{convertedName}</span>;
                  } else if (element.type === 'number') {
                    return <span key={index}>{element.value}</span>;
                  } else if (element.type === 'operator') {
                    return <span key={index}> {element.symbol} </span>;
                  }
                  return null;
                };
                
                const formulaParts = parsedElements.map((element, index) => renderElement(element, index));
                
                return (
                  <div key={expression.id} className="expression-display">
                    <span className="expression-formula">{formulaParts}</span>
                    {adjustedResult !== null && adjustedResult !== undefined && (
                      <span className="expression-result"> = {adjustedResult}</span>
                    )}
                  </div>
                );
              })}
            </>
          )}
        </div>
            </>
          );
        })()}
      </div>
    </div>
  );
}
export default GeometryModelView;
